#if !defined(SPHEREBOX_HPP)
#define SPHEREBOX_HPP

#include <memory>

#include "Eigen"

#include "Object.hpp"
#include "Material.hpp"
#include "Lambertian.hpp"

class SphereBox :public Object {
private:
    // 圆心位置
    Eigen::Vector3f pos;
    // 半径
    float ORadius, IRadius;
    // 材质指针
    std::shared_ptr<Material> mat;

public:
    SphereBox() {}
    SphereBox(const Eigen::Vector3f& position, float InRadius, float OutRadius)
        :pos(position), IRadius(InRadius),ORadius(OutRadius), mat(std::make_shared<Lambertian>(Lambertian()))
    {}
    SphereBox(const Eigen::Vector3f& position, float InRadius, float OutRadius, std::shared_ptr<Material> material)
        :pos(position), IRadius(InRadius), ORadius(OutRadius), mat(material)
    {}
    std::optional<ShadePayload> hit(const Ray& ray)const override {
        std::optional<ShadePayload> payload;
        float a = 1;
        float b = 2 * (ray.direction().dot(ray.origin() - pos));
        float cO = ray.origin().dot(ray.origin()) - 2 * pos.dot(ray.origin()) + pos.dot(pos) - ORadius * ORadius;
        float deltaO = b * b - 4 * a * cO;
        float cI = ray.origin().dot(ray.origin()) - 2 * pos.dot(ray.origin()) + pos.dot(pos) - IRadius * IRadius;
        float deltaI = b * b - 4 * a * cI;
        if (deltaO >= 0) {
            float t1 = (-b - sqrt(deltaO)) / (2 * a);
            float t2 = (-b + sqrt(deltaO)) / (2 * a);
            payload.emplace();
            payload->mat = this->mat;
            if (t1 >= epsilon && t2 >= epsilon) {
                payload->tNear = t1;
                payload->pos = ray.at(t1);
                payload->normal = (payload->pos - pos).normalized();
                payload->isFrontFace = true;
            }
            else if (t1 < 0 && t2 >= epsilon) {
                if (deltaI >= epsilon) {
                    float ti1 = (-b - sqrt(deltaI)) / (2 * a);
                    float ti2 = (-b + sqrt(deltaI)) / (2 * a);
                    if (ti1 >= epsilon && ti2 >= epsilon) {
                        payload->tNear = ti1;
                        payload->pos = ray.at(ti1);
                        payload->normal = (payload->pos - pos).normalized();
                        payload->isFrontFace = false;
                    }
                    else if (ti1 < 0 && ti2 >= epsilon) {
                        payload->tNear = ti2;
                        payload->pos = ray.at(ti2);
                        payload->normal = (pos - payload->pos).normalized();
                        payload->isFrontFace = true;
                    }
                }
                payload->tNear = t2;
                payload->normal = (pos - ray.at(payload->tNear)).normalized();
                payload->pos = ray.at(t2);
                payload->isFrontFace = false;
            }
            else {
                payload.reset();
            }
        }

        return payload;
    }
};

#endif // SPHEREBOX_HPP
